package com.itheima.stock.service.impl;

import com.alibaba.excel.EasyExcel;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.itheima.stock.domain.InnerMarketDomain;
import com.itheima.stock.domain.Stock4EvrDayDomain;
import com.itheima.stock.domain.Stock4MinuteDomain;
import com.itheima.stock.domain.StockUpdownDomain;
import com.itheima.stock.mapper.*;
import com.itheima.stock.domain.Stock4OuterMarketDomain;
import com.itheima.stock.pojo.domain.StockBlockDomain;
import com.itheima.stock.pojo.entity.StockRtInfo;
import com.itheima.stock.pojo.vo.*;
import com.itheima.stock.service.StockService;
import com.itheima.stock.utils.DateTimeUtil;
import com.itheima.stock.vo.resp.PageResult;
import com.itheima.stock.vo.resp.R;
import com.itheima.stock.vo.resp.ResponseCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.joda.time.DateTime;
import org.joda.time.DateTimeConstants;
import org.joda.time.format.DateTimeFormat;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 股票服务实现
 */
@Service
@Slf4j
public class StockServiceImpl implements StockService {

    @Autowired
    private StockInfoConfig stockInfoConfig;

    @Autowired
    private StockMarketIndexInfoMapper stockMarketIndexInfoMapper;

    @Autowired
    private StockRtInfoMapper stockRtInfoMapper;

    /**
     * 注入本地缓存bean
     */
    @Autowired
    private Cache<String,Object> caffeineCache;

    @Autowired
    private StockOuterMarketIndexInfoMapper stockOuterMarketIndexInfoMapper;

    @Autowired
    private StockBlockRtInfoMapper stockBlockRtInfoMapper;

    @Autowired
    private StockBusinessMapper stockBusinessMapper;

    /**
     * 获取国内大盘最新的数据
     * @return
     */
    @Override
    public R<List<InnerMarketDomain>> getInnerMarketInfo() {
        //默认本地缓存加载数据，如果不存在则从数据库加载并同步到本地缓存
        //在开盘周期内，本地缓存你默认有效期1分钟
        R<List<InnerMarketDomain>> result = (R<List<InnerMarketDomain>>) caffeineCache.get("innerMarketKey",key->{
            //1、获取股票最新的交易时间点（精确到分钟，秒和毫秒置为0）
            Date curDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
            //mock data 等后续完成股票采集job工程，再将代码删除即可
            curDate = DateTime.parse("2021-12-28 09:31:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
            //2、获取大盘编码集合
            List<String> mCodes = stockInfoConfig.getInner();
            //3、调用mapper查询数据
            List<InnerMarketDomain> data = stockMarketIndexInfoMapper.getMarketInfo(curDate,mCodes);
            //4、封装并响应
            return R.ok(data);
        });

        return result;
    }

    @Override
    public R<PageResult<StockUpdownDomain>> getStockInfoByPage(Integer page, Integer pageSize) {
        //1、获取股票最新的交易时间点（精确到分钟，秒和毫秒置为0）
        Date curDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
        //mock data 等后续完成股票采集job工程，再将代码删除即可
        curDate = DateTime.parse("2021-12-28 09:42:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //2.设置PageHelper分页参数
        PageHelper.startPage(page,pageSize);
        //3.调用mapper查询
        List<StockUpdownDomain> pageData = stockRtInfoMapper.getStockInfoByTime(curDate);
        //4.组装PageResult对象
        PageInfo<StockUpdownDomain> pageInfo = new PageInfo<>(pageData);
        PageResult<StockUpdownDomain> pageResult = new PageResult<StockUpdownDomain>(pageInfo);
        //5.响应数据
        return R.ok(pageResult);
    }

    @Override
    public R<Map<String, List>> getStockUpDownCount() {
        //1、获取股票最新的交易时间点（精确到分钟，秒和毫秒置为0）
        DateTime curDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //mock data 等后续完成股票采集job工程，再将代码删除即可
        curDateTime = DateTime.parse("2022-01-06 14:25:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date endDate = curDateTime.toDate();
        //2.获取最新交易时间点对应的开盘时间点
        Date startDate = DateTimeUtil.getOpenDate(curDateTime).toDate();
        //3.统计涨停数据
        List<Map> upList = stockRtInfoMapper.getStockUpdownCount(startDate,endDate,1);
        //4.统计跌停
        List<Map> downList = stockRtInfoMapper.getStockUpdownCount(startDate,endDate,0);
        //5.组装数据
        HashMap<String, List> info = new HashMap<>();
        info.put("upList",upList);
        info.put("downList",downList);
        //6.响应数据
        return R.ok(info);

    }


    /**
     * 到处指定页码的最新股票信息
     * @param page 当前页
     * @param pageSize 每页大小
     * @param response
     */
    @Override
    public void exportStockUpDownInfo(Integer page, Integer pageSize, HttpServletResponse response) {
        //1.获取分页数据
        R<PageResult<StockUpdownDomain>> r = this.getStockInfoByPage(page, pageSize);
        List<StockUpdownDomain> rows = r.getData().getRows();
        //2.将数据导出到excel中
        // 这里注意 有同学反应使用swagger 会导致各种问题，请直接用浏览器或者用postman
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        String fileName = null;
        try {
            fileName = URLEncoder.encode("股票信息表", "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
            EasyExcel.write(response.getOutputStream(), StockUpdownDomain.class).sheet("股票涨幅信息").doWrite(rows);
        } catch (IOException e) {
            log.error("当前页码：{}，每页大小：{}，当前时间：{}，异常信息：{}",page,pageSize,DateTime.now().toString("yyyy-MM-dd HH:mm:ss"),e.getMessage());
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            R<Object> error = R.error(ResponseCode.CHECK_CODE_ERROR);
            try {
                String jsonData = new ObjectMapper().writeValueAsString(error);
                response.getWriter().write(jsonData);
            } catch (IOException ioException) {
                log.error("exportStockUpDownInfo:响应错误信息失败,时间：{}",DateTime.now().toString("yyyy-MM-dd HH:mm:ss"));
            }

        }


    }

    /**
     * 统计大盘T日和T-1日每分钟交易量的统计
     * @return
     */
    @Override
    public R<Map<String, List>> getComparedStockTradeAmt() {
        //1.获取T日（最新股票交易日的日期范围）
        DateTime tEndDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());

        //mock data
        tEndDateTime = DateTime.parse("2022-01-03 14:40:00",DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));

        Date tEndDate = tEndDateTime.toDate();
        //开盘时间
        Date tStartDate = DateTimeUtil.getOpenDate(tEndDateTime).toDate();
        //2.获取T-1日的时间范围
        DateTime preTEndDateTime = DateTimeUtil.getPreviousTradingDay(tEndDateTime);

        //mock data
        preTEndDateTime = DateTime.parse("2022-01-02 14:40:00",DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));


        Date preTEndDate = preTEndDateTime.toDate();
        //开盘时间
        Date tPreStartDate = DateTimeUtil.getOpenDate(preTEndDateTime).toDate();
        //3.调用mapper查询
        //3.1 统计T日
        List<Map> tData = stockMarketIndexInfoMapper.getSumAmtInfo(tStartDate,tEndDate,stockInfoConfig.getInner());
        //3.2 统计T-1日
        List<Map> preTData = stockMarketIndexInfoMapper.getSumAmtInfo(tPreStartDate,preTEndDate,stockInfoConfig.getInner());
        //4.组装数据
        HashMap<String,List> info = new HashMap<>();
        info.put("amtList",tData);
        info.put("yesAmtList",preTData);
        //5.响应数据
        return R.ok(info);
    }


    /**
     * 统计最新交易时间点下股票（A股）在哥哥涨幅区间的数量
     * @return
     */
    @Override
    public R<Map> getIncreaseRangeInfo() {
        //1.获取当前最新的股票交易时间点
        DateTime curDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());

        //mock data
        curDateTime = DateTime.parse("2022-01-06 09:55:00",DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));

        Date curDate = curDateTime.toDate();

        //2.掉用mapper获取数据
        List<Map> infos = stockRtInfoMapper.getIncreaseRangeInfoByDate(curDate);
        //获取有序的涨幅区间标题集合
        List<String> upDownRange = stockInfoConfig.getUpDownRange();
//        List<Map> allInfos = new ArrayList<>();
        //将顺序的涨幅区间内的每个元素转换成Map对象即可
//        for(String title : upDownRange){
//            Map tmp = null;
//            for (Map info : infos) {
//                if (info.containsValue(title)) {
//                    tmp=info;
//                    break;
//                }
//            }
//            if(tmp==null){
//                //不存在，则补齐
//                tmp=new HashMap();
//                tmp.put("count",0);
//                tmp.put("title",title);
//            }
//            allInfos.add(tmp);
//        }
        //方式2：stream遍历获取
        List<Map> allInfos = upDownRange.stream().map(title -> {
            Optional<Map> result = infos.stream().filter(map -> map.containsValue(title)).findFirst();
            if (result.isPresent()) {
                return result.get();
            } else {
                HashMap<String, Object> tmp = new HashMap<>();
                tmp.put("count", 0);
                tmp.put("title", title);
                return tmp;
            }
        }).collect(Collectors.toList());

        //3.组装数据
        HashMap<String,Object> data = new HashMap<>();
        data.put("time",curDateTime.toString("yyyy-MM-dd HH:mm:ss"));
        data.put("infos",allInfos);
        //4.响应
        return R.ok(data);
    }

    /**
     * 获取指定股票T日的分时数据
     * @param stockCode 股票编码
     * @return
     */
    @Override
    public R<List<Stock4MinuteDomain>> getStockScreenTimeSharing(String stockCode) {
        //1.获取T日最新股票价便宜时间点 endTime
        DateTime endDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());

        //mock data
        endDateTime = DateTime.parse("2021-12-30 14:30:00",DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));

        Date endDate = endDateTime.toDate();
        Date openDate = DateTimeUtil.getOpenDate(endDateTime).toDate();
        //2.查询
        List<Stock4MinuteDomain> data = stockRtInfoMapper.getStock4MinuteInfo(openDate,endDate,stockCode);
        //3.返回
        return R.ok(data);
    }

    /**
     * 统计指定股票的日K线数据
     * @param stockCode 股票编码
     * @return
     */
    @Override
    public R<List<Stock4EvrDayDomain>> getStockScreenDkLine(String stockCode) {
        //1.获取统计日K线的数据的时间范围
        //1.1获取截止时间
        DateTime endDateTime = DateTimeUtil.getLastDate4Stock(DateTime.now());
        //mock data
        endDateTime = DateTime.parse("2022-06-06 14:25:00",DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date endDate = endDateTime.toDate();
        //1.2起始时间
        DateTime startDateTIme = endDateTime.minusMonths(3);
        startDateTIme = DateTime.parse("2022-01-01 09:30:00",DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        Date startDate = startDateTIme.toDate();
        //2.调用mapper获取指定日期范围内的日K线数据
        List<Stock4EvrDayDomain> dkLineData = stockRtInfoMapper.getStock4DkLine(startDate,endDate,stockCode);
        //3.返回
        return R.ok(dkLineData);
    }


    /**
     * 外盘指数行情数据查询，根据时间和大盘点数降序排序取前4
     * @return
     */
    @Override
    public R<List<Stock4OuterMarketDomain>> getStock4OuterInfo() {
        List<Stock4OuterMarketDomain> stock4OuterMarketDomains = stockOuterMarketIndexInfoMapper.select4ByTimeAndRose();

        return R.ok(stock4OuterMarketDomains);
    }


    /**
     *需求说明: 沪深两市板块分时行情数据查询，以交易时间和交易总金额降序查询，取前10条数据
     * @return
     */
    @Override
    public R<List<StockBlockDomain>> sectorAllLimit() {
        //获取股票最新交易时间点
        Date lastDate = DateTimeUtil.getLastDate4Stock(DateTime.now()).toDate();
        //TODO mock数据,后续删除
        lastDate=DateTime.parse("2021-12-21 14:30:00", DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")).toDate();
        //1.调用mapper接口获取数据
        List<StockBlockDomain> infos=stockBlockRtInfoMapper.sectorAllLimit(lastDate);
        //2.组装数据
        if (CollectionUtils.isEmpty(infos)) {
            return R.error(ResponseCode.NO_RESPONSE_DATA.getMessage());
        }
        return R.ok(infos);
    }


    /**
     * 模糊搜索，根据传入的股票代码模糊搜索股票数据
     * @param code
     * @return
     */
    @Override
    public R<List<StockSearchInfo>> searchStock(String code) {
        List<StockSearchInfo> result = stockBusinessMapper.searchStock(code);
        return R.ok(result);
    }

    /**
     * 个股主营业务查询
     * @param code
     * @return
     */
    @Override
    public R<StockDescribeInfo> getStockDescribe(String code) {
        StockDescribeInfo result = stockBusinessMapper.getStockDescribe(code);
        return R.ok(result);
    }

    /**
     *  统计每周内的股票数据信息，信息包含：
     * 	股票ID、 一周内最高价、 一周内最低价 、周1开盘价、周5的收盘价、
     * 	整周均价、以及一周内最大交易日期（一般是周五所对应日期）
     * @param code
     * @return
     */
    @Override
    public R<StockRtWeeklyInfo> getStockRtWeeklyInfo(String code) {
        // 获取当前日期时间
        DateTime today = DateTime.now();
        //mock data
        today = DateTime.parse("2021-12-30 14:00:00",DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        // 获取本周一的日期时间
        List<DateTime> arr = new ArrayList<>();
        DateTime monday = today.withDayOfWeek(DateTimeConstants.MONDAY);
        // 计算从本周一到今天的工作日日期
        for (DateTime date = monday; date.isBefore(today.plusDays(1)); date = date.plusDays(1)) {
            if (date.getDayOfWeek() >= DateTimeConstants.MONDAY && date.getDayOfWeek() <= DateTimeConstants.FRIDAY) {
                arr.add(DateTimeUtil.getCloseDate(date));
            }
        }
        DateTime lastTime = arr.get(arr.size()-1);
        arr.remove(arr.size()-1);
        lastTime = DateTimeUtil.getLastDate4Stock(today);
        arr.add(lastTime);
        List<Date> arr2 = arr.stream().map(date -> date.toDate()).collect(Collectors.toList());
        //1.调用mapper接口获取数据
        List<StockRtInfo> result = stockRtInfoMapper.getStockDkLine(code,arr2);
        StockRtWeeklyInfo stockRtWeeklyInfo = new StockRtWeeklyInfo();
        //处理为空时的特殊情况
        if(result.isEmpty()) return R.ok();
        StockRtInfo first = result.get(0);
        StockRtInfo last = result.get(result.size()-1);
        stockRtWeeklyInfo.setStockCode(first.getStockCode());
        stockRtWeeklyInfo.setMaxPrice(first.getMaxPrice());
        stockRtWeeklyInfo.setMxTime(lastTime.toDate());
        BigDecimal sum = new BigDecimal(0);
        for(int i=1;i<result.size();++i){
            StockRtInfo stockRtInfo = result.get(i);
//            sum+=stockRtInfo.get
        }

        return R.ok(stockRtWeeklyInfo);
    }

    /**
     *  统计每周内的股票数据信息，信息包含：
     * 	股票ID、 一周内最高价、 一周内最低价 、周1开盘价、周5的收盘价、
     * 	整周均价、以及一周内最大交易日期（一般是周五所对应日期）
     * @param code
     * @return
     */
    @Override
    public R<StockRt> getStockRtRecentInfo(String code) {
        // 获取当前日期时间
        DateTime today = DateTime.now();
        //mock data
        today = DateTime.parse("2022-01-05 10:11:00",DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        today = DateTimeUtil.getLastDate4Stock(today);

        return R.ok(stockRtInfoMapper.getStockRtRecentInfo(code,today.toDate()));
    }

    /**
     * 个股交易流水行情数据查询--查询最新交易流水，按照交易时间降序取前10
     * @return
     */
    @Override
    public R<List<StockRtScreen>> getStockRtScreenSecond(String code) {
        // 获取当前日期时间
        DateTime today = DateTime.now();
        //mock data
        today = DateTime.parse("2022-01-05 10:11:00",DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"));
        today = DateTimeUtil.getLastDate4Stock(today);
        List<StockRtScreen> results = stockRtInfoMapper.getStockRtScreenSecond(code,today.toDate());

        return R.ok(results);
    }


}
